﻿using System;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace AgentTest
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic["aa"] = "as";
            dic["aa"] = "ab";
            Wdad wdad = new Wdad();

        }
    }

    public interface IWdadModule
    {
        void OnStart();
        void OnStop();
        void OnSessionChange(SessionChangeDescription changeDescription);
        string PausedForDiskSpaceMessage { get; }
    }

    public class WashModule : IWdadModule
    {
        #region IWdadModule Members

        public void OnStart()
        {
            throw new NotImplementedException();
        }

        public void OnStop()
        {
            throw new NotImplementedException();
        }

        public void OnSessionChange(SessionChangeDescription changeDescription)
        {
            throw new NotImplementedException();
        }

        public string PausedForDiskSpaceMessage
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }

    public class Wdad : IBinder
    {
        AgentState m_currentState = AgentState.Unknown;
        object m_mutex = new object();

        string m_currentStateDescription = string.Empty;
        DateTime m_startupTime;
        Stopwatch m_UpTimeStopWatch;
        Broker m_broker;
        const string m_unrecognizedAgent = "The Collector cannot recognize the Agent. The Collector may need to be upgraded";

        Timer m_sessionPingTimer;
        TimeSpan m_sessionPingInterval = TimeSpan.FromSeconds(60);
        
        AgentState CurrentState
        {
            get
            {
                lock (m_mutex)
                {
                    return m_currentState;
                }
            }
            set
            {
                lock (m_mutex)
                {
                    if (value != m_currentState)
                    {
                        //Log.Information("Changing current state from {0} to {1}", m_currentState, value);
                        m_currentState = value;
                        //SetRegistryCurrentState(value);
                    }
                }
            }
        }

        string CurrentStateDescription
        {
            get
            {
                return m_currentStateDescription;
            }
            set
            {
                if (value != m_currentStateDescription)
                {
                    //Log.Information("Setting current state description to '{0}'", value);
                    m_currentStateDescription = value;
                    //SetRegistryCurrentStateDescription(value);
                }
            }
        }

        public Wdad()
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            m_sessionPingTimer = new Timer(SessionPing);
        }

        public IList<ServiceEndpoint> GetServers()
        {
            throw new NotImplementedException();
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            //Event.Error("The DirectAudit Agent encountered a serious error and must stop: {0}", e.ExceptionObject);
            //MiniDumpWriter.WriteMiniDumpAtCrash("Wdad");
        }
        
        void SessionPing(object state)
        {
            try
            {
                //Log.Verbose("Sending a session ping");
                //SessionPingMessage2 sessionPingMessage = new SessionPingMessage2(m_sessionPingInterval, CurrentState, CurrentStateDescription, m_startupTime, m_UpTimeStopWatch.Elapsed.Ticks);
                //m_broker.SendApplicationMessage(sessionPingMessage.ToArray());
                //if (CurrentStateDescription.Equals(m_unrecognizedAgent))
                //    CurrentStateDescription = string.Empty;
            }
            catch (Exception ex)
            {
                //Log.Verbose("Unable to send periodic session ping: {0}", ex);
                CurrentStateDescription = m_unrecognizedAgent;
            }
            finally
            {
                m_sessionPingTimer.Change(m_sessionPingInterval, TimeSpan.FromMilliseconds(1).Negate());
            }
        }

        public void Start()
        {
            try
            {
                //// Log starting event.
                //Event.Information("wdad starting");
                //m_startupTime = DateTime.UtcNow;

                //m_UpTimeStopWatch = new Stopwatch();
                //m_UpTimeStopWatch.Start();

                //// Make sure agent is running under the system account.
                //WindowsIdentity agentIdentity = WindowsIdentity.GetCurrent();
                //if (agentIdentity == null || agentIdentity.User != new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null))
                //{
                //    string message = SafeFormat.Format(
                //        "The agent can only be run under the 'Local System' account.  " +
                //        "It is currently configured to run under the account '{0}'.  " +
                //        "Please use the Services control panel to reconfigure the Agent.",
                //        agentIdentity == null ? "<unknown>" : agentIdentity.Name);
                //    throw new Exception(message);
                //}

                //// Clear out the "current" values in the registry.
                //ClearCurrentSettings();

                //// Set state.
                //CurrentState = AgentState.Initializing;

                //// Load client side modules.
                //try
                //{
                //    LoadModules();
                //}
                //catch (Exception)
                //{
                //    try
                //    {
                //        StopModules();
                //    }
                //    catch (Exception ex2)
                //    {
                //        Log.Verbose("Unable to stop modules: {0}", ex2);
                //    }
                //    throw;
                //}

                //// Monitor changes in logging level.
                //m_logLevelWatcher.Start();
            }
            catch (Exception ex)
            {
                //Event.Error("DirectAudit Agent was unable to start: {0}", ex);
                throw;
            }
        }

        internal void LoadModules()
        {
            //Log.Verbose("wdad loading modules");

            //WashManager.StopAllOrphanedWashesIfAny();

            //// Start broker.
            //m_broker = new Broker(this,
            //                      Path.Combine(AgentSettings.SpoolDir, "wash"),
            //                      AgentSettings.ForceRebindInterval,
            //                      AgentSettings.ReadTimeout);
            //m_broker.BoundStateChanged += BoundStateChanged;
            //m_broker.Start();

            //// Start modules.            
            //m_modules.Add(new WashModule(m_broker));
            //foreach (IWdadModule module in m_modules)
            //{
            //    module.OnStart();
            //}

        }

        internal void StopModules()
        {
            Log.Verbose("wdad stopping modules");
            foreach (IWdadModule module in m_modules)
            {
                module.OnStop();
            }
            m_broker.Stop();
        }
    }

    public class ServiceEndpoint
    {
        string m_hostname;
        int m_port;
        string m_servicePrincipalName;

        public ServiceEndpoint(string hostname, int port, string servicePrincipalName)
        {
            Initialize(hostname, port, servicePrincipalName);
        }

        public ServiceEndpoint(string hostname, string portAndSpn)
        {
            Initialize(hostname, portAndSpn);
        }

        public ServiceEndpoint(string hostPortAndSpn)
        {
            if (string.IsNullOrEmpty(hostPortAndSpn))
                throw new ArgumentException("Service endpoint string may not be null or empty", "hostPortAndSpn");
            int colonPosition = hostPortAndSpn.IndexOf(':');
            if (colonPosition < 0)
                throw new ArgumentException(string.Format("Invalid service endpoint string.  Expected 'host:port:SPN', not '{0}'",
                                                          hostPortAndSpn));
            string hostname = hostPortAndSpn.Substring(0, colonPosition);
            string portAndSpn = hostPortAndSpn.Substring(colonPosition + 1);
            Initialize(hostname, portAndSpn);
        }

        public string Hostname { get { return m_hostname; } }

        public int Port { get { return m_port; } }

        public string ServicePrincipalName { get { return m_servicePrincipalName; } }

        public bool HostAndSpnMatches(ServiceEndpoint serviceEndpoint)
        {
            if (serviceEndpoint == null)
                throw new ArgumentNullException("serviceEndpoint");
            return
                string.Compare(m_hostname, serviceEndpoint.Hostname, true) == 0 &&
                string.Compare(m_servicePrincipalName, serviceEndpoint.ServicePrincipalName, true) == 0;
        }

        override public string ToString()
        {
            return string.Format("{0}:{1}:{2}", m_hostname, m_port, m_servicePrincipalName);
        }


        void Initialize(string hostname, int port, string servicePrincipalName)
        {

            if (string.IsNullOrEmpty(hostname))
                throw new ArgumentNullException("hostname");
            if (port <= 0)
                throw new ArgumentException("Port must be greater than zero", "port");
            if (string.IsNullOrEmpty(servicePrincipalName))
                throw new ArgumentNullException("servicePrincipalName");

            m_hostname = hostname;
            m_port = port;
            m_servicePrincipalName = servicePrincipalName;
        }

        void Initialize(string hostname, string portAndSpn)
        {
            if (string.IsNullOrEmpty(portAndSpn))
                throw new ArgumentNullException("portAndSpn");
            int colonPosition = portAndSpn.IndexOf(':');
            if (colonPosition < 0)
                throw new ArgumentException(string.Format("Invalid service binding information.  Expected 'port:SPN', not '{0}'",
                                                          portAndSpn),
                                            "portAndSpn");
            string portString = portAndSpn.Substring(0, colonPosition);
            int port;
            bool parsed = int.TryParse(portString, out port);
            if (!parsed)
                throw new ArgumentException(string.Format("Invalid service binding information.  Expected integer port, not '{0}'",
                                                          portString));
            string servicePrincipalName = portAndSpn.Substring(colonPosition + 1);
            Initialize(hostname, port, servicePrincipalName);
        }
    }
}
